﻿using System;
using System.Web;
using Glimpse.AspNet;
using Glimpse.Core.Extensibility;
using Glimpse.Core.Framework;

namespace Glimpse.Config
{
    // HACK: We'll open up Glimpse to make the out of the box HttpModule work in this scenario so that this code can be deleted.
    // This is essentially a copy and paste of the out of the box HttpModule, simplified with one change detailed below.
    public class OrchardHttpModule : IHttpModule
    {
        private const string RuntimeKey = "__GlimpseRuntime";
        private const string LoggerKey = "__GlimpseLogger";
        private static readonly object LockObj = new object();
        private static readonly Factory Factory;

        static OrchardHttpModule()
        {
            var serviceLocator = new AspNetServiceLocator();
            Factory = new Factory(serviceLocator);
        }

        public void Init(HttpApplication httpApplication)
        {
            Init(WithTestable(httpApplication));
        }

        public void Dispose()
        {
            // Nothing to dispose
        }

        internal void Init(HttpApplicationBase httpApplication)
        {
            var runtime = GetRuntime(httpApplication.Application);

            AppDomain.CurrentDomain.SetData(LoggerKey, Factory.InstantiateLogger());

            if (runtime.IsInitialized || runtime.Initialize())
            {
                httpApplication.BeginRequest += (context, e) => BeginRequest(WithTestable(context));
                httpApplication.PostAcquireRequestState += (context, e) => BeginSessionAccess(WithTestable(context));
                httpApplication.PostRequestHandlerExecute += (context, e) => EndSessionAccess(WithTestable(context));
                httpApplication.PostReleaseRequestState += (context, e) => EndRequest(WithTestable(context));
            }
        }

        internal IGlimpseRuntime GetRuntime(HttpApplicationStateBase applicationState)
        {
            var runtime = applicationState[RuntimeKey] as IGlimpseRuntime;

            if (runtime == null)
            {
                lock (LockObj)
                {
                    runtime = applicationState[RuntimeKey] as IGlimpseRuntime;

                    if (runtime == null)
                    {
                        // HACK: Due to a current bug in Factory, I'm just overriding the config here before creating the runtime.
                        var config = Factory.InstantiateConfiguration() as GlimpseConfiguration;
                        config.EndpointBaseUri = "~/Glimpse";
                        config.DefaultRuntimePolicy = RuntimePolicy.On;

                        runtime = new GlimpseRuntime(config);

                        applicationState.Add(RuntimeKey, runtime);
                    }
                }
            }

            return runtime;
        }

        internal void BeginRequest(HttpContextBase httpContext)
        {
            // TODO: Add Logging to either methods here or in Runtime
            var runtime = GetRuntime(httpContext.Application);

            runtime.BeginRequest();
        }

        internal void EndRequest(HttpContextBase httpContext)
        {
            var runtime = GetRuntime(httpContext.Application);

            runtime.EndRequest();
        }

        private static HttpContextBase WithTestable(object sender)
        {
            var httpApplication = sender as HttpApplication;

            return new HttpContextWrapper(httpApplication.Context);
        }

        private static HttpApplicationBase WithTestable(HttpApplication httpApplication)
        {
            return new HttpApplicationWrapper(httpApplication);
        }

        private void BeginSessionAccess(HttpContextBase httpContext)
        {
            var runtime = GetRuntime(httpContext.Application);

            runtime.BeginSessionAccess();
        }

        private void EndSessionAccess(HttpContextBase httpContext)
        {
            var runtime = GetRuntime(httpContext.Application);

            runtime.EndSessionAccess();
        }
    }
}